local mhr_type = {
    library   = "library",
    namespace = "namespace",
    template  = "template",
    class     = "class",
    enum      = "enum",
    instance  = "instance",
    method    = "method",
    attribute = "attribute",
}

local mhr = { _type_ = mhr_type.library, }

local symbol = {
    cache    = "meet_your_mods.symbol",
    class    = {},
    template = {},
}

--预载所有符号
function symbol:load()
    local cache = json.load_file(self.cache)
    if not cache then
        return
    end

    if cache.class then
        for name, content in pairs(cache.class) do
            self:load_class(self:convert_to_class(name), content)
        end
    end

    if cache.template then
        for name, arguments in pairs(cache.template) do
            for argument, content in pairs(arguments) do
                self:load_class(self:convert_to_class(name)[argument], content)
            end
        end
    end
end

function symbol:load_class(class, content)
    if content.attribute then
        for _, attribute in pairs(content.attribute) do
            self.fetch_attribute(class, attribute)
        end
    end

    if content.method then
        for method, user_names in pairs(content.method) do
            for user_name, _ in pairs(user_names) do
                class:fetch_method(user_name, method)
            end
        end
    end

    if content.enum then
        for enum, user_names in pairs(content.enum) do
            for user_name, _ in pairs(user_names) do
                class:fetch_enum(user_name, enum)
            end
        end
    end
end

--将所有缓存的符号写入文件
function symbol:dump()
    local cache = { class = self.class, template = self.template, }
    json.dump_file(self.cache, cache)
end

--缓存属性
function symbol:cache_attribute(class, name)
    local cache = self:get_class_cache(class)
    table.insert(cache.attribute, name)
end

--缓存枚举
function symbol:cache_enum(class, name, user_name)
    local cache = self:get_class_cache(class)
    local enum  = cache.enum[name]

    if not enum then
        enum = {}
        cache.enum[name] = enum
    end
    enum[user_name] = true
end

--缓存方法
function symbol:cache_method(class, signature, user_name)
    local cache  = self:get_class_cache(class)
    local method = cache.method[signature]

    if not method then
        method = {}
        cache.method[signature] = method
    end
    method[user_name] = true
end

function symbol:get_class_cache(class)
    local cache = self.class[class._name_]

    local generic  = rawget(class, "_generic_")
    local argument = rawget(class, "_argument_")
    if generic and argument then
        local template = self.template[generic]
        if not template then
            template = {}
            self.template[generic] = template
        end

        cache = template[argument]
        if not cache then
            cache = { attribute = {}, method = {}, enum = {}, }
            template[argument] = cache
        end
    end

    if not cache then
        cache = { attribute = {}, method = {}, enum = {}, }
        self.class[class._name_] = cache
    end

    return cache
end

--将给定sdk类型构造为mhr类型
function symbol:make_class(typedef)
    --处理System.IntX、System.UIntX、System.Single等原始类型
    if typedef:is_primitive() then
        return nil
    end

    --处理可被reframework自动处理的非原始类型
    if symbol:is_auto_converted(typedef) then
        return nil
    end

    --处理模板类型
    local template, class_name = symbol:get_template_config(typedef)
    if template then
        return template[class_name]
    end

    --处理常规类型
    return self:convert_to_class(class_name)
end

--检测是否为可被reframework自动处理的非原始类型
function symbol:is_auto_converted(typedef)
    local name = typedef:get_full_name()
    for _, class_name in pairs({ "System.Void", "System.String", "via.vec2", "via.vec3", "via.vec4", "via.mat4", }) do
        if name == class_name then
            return true
        end
    end

    return false
end

--获取模板类型和实例化参数
function symbol:get_template_config(typedef)
    local name = typedef:get_full_name()

    --处理数组类型
    if "[]" == string.sub(name, -2) then
        return mhr.System.Array, string.sub(name, 1, -3)
    end

    --处理非模板类型
    if not typedef:is_generic_type() then
        return nil, name
    end

    --处理其他模板类型
    local template_name = typedef:get_generic_type_definition():get_full_name()
    template_name = string.sub(template_name, 1, select(1, string.find(template_name, "`", 1, true)) - 1)

    --组装实例化参数
    local argument = ""
    for _, typedef in pairs(typedef:get_generic_argument_types()) do
        argument = argument .. "," .. typedef:get_full_name()
    end
    argument = string.sub(argument, 2)

    return symbol:convert_to_class(template_name), argument
end

--将给定命名空间名称转换为类型
function symbol:convert_to_class(name)
    local namespace = mhr
    local head      = 1

    for index in function() return string.find(name, ".", head, true) end do
        namespace = namespace[string.sub(name, head, index - 1)]
        head      = index + 1
    end

    return namespace[string.sub(name, head)]
end

--获取sdk维护的单实例
function symbol.fetch_singleton(class)
    local sdk_singleton = sdk.get_managed_singleton(class._name_)
    if not sdk_singleton then
        return nil
    end

    local singleton = rawget(class, "_singleton_")
    if singleton then
        return singleton(sdk.get_managed_singleton(class._name_))
    end

    --生成新的单实例
    singleton = class(sdk.get_managed_singleton(class._name_))
    rawset(class, "_singleton_", singleton)

    return singleton
end

function symbol.get_singleton(class, reload)
    return reload and symbol.fetch_singleton(class) or rawget(class, "_singleton_") or symbol.fetch_singleton(class)
end

function symbol.new(class)
    return class(class._typedef_:create_instance())
end

--将sdk的枚举值以默认或给定名称维护到mhr类型中
function symbol.fetch_enum(class, key, sdk_key, sdk_def)
    --读取已经设置过的枚举
    local value = rawget(class, key)
    if value ~= nil then
        return value
    end

    --读取已经被设置为其他名称的枚举
    if sdk_key then
        value = class[sdk_key]
        rawset(class, key, value)
        symbol:cache_enum(class, sdk_key, key)

        return value
    end

    value = sdk_def:get_data()
    rawset(class, key, value)
    symbol:cache_enum(class, key, key)

    return value
end

--将sdk的属性维护到mhr类型中
function symbol.fetch_attribute(class, key, sdk_def)
    if not sdk_def then
        sdk_def = class._typedef_:get_field(key)
    end

    --获取属性类型
    local field_type = symbol:make_class(sdk_def:get_type())
    if field_type and mhr_type.enum == field_type._type_ then
        field_type = nil
    end

    --构造getter
    local getter = field_type
        and function(instance) return field_type(sdk_def:get_data(instance._instance_)) end
        or function(instance) return sdk_def:get_data(instance._instance_) end

    local bundle = {
        _type_    = mhr_type.attribute,
        _typedef_ = sdk_def,
        _getter_  = getter,
    }
    rawset(class, key, bundle)
    symbol:cache_attribute(class, key)

    return bundle
end

--将sdk的方法以默认或给定名称维护到mhr类型中
function symbol.fetch_method(class, key, signature, sdk_def)
    local bundle = rawget(class, key)
    if bundle then
        return bundle
    end

    if signature then
        bundle = class[signature]
        rawset(class, key, bundle)
        symbol:cache_method(class, signature, key)

        return bundle
    end

    --获取返回值类型
    local return_type = symbol:make_class(sdk_def:get_return_type())
    if return_type and mhr_type.enum == return_type._type_ then
        return_type = nil
    end

    --构造方法
    local instance_bundle = return_type
        and function(instance, ...) return return_type(sdk_def(instance._instance_, ...)) end
        or function(instance, ...) return sdk_def(instance._instance_, ...) end
    local static_bundle   = return_type
        and function(_, _, ...) return return_type(sdk_def(nil, ...)) end
        or function(_, _, ...) return sdk_def(nil, ...) end

    local bundle = {
        _type_        = mhr_type.method,
        _typedef_     = sdk_def,
        _getter_      = instance_bundle,
        sdk_hook      = symbol.sdk_hook,
        sdk_auto_hook = symbol.sdk_auto_hook,
    }

    if sdk_def:is_static() then
        setmetatable(bundle, { __call = static_bundle, })
    else
        setmetatable(bundle, { __call = function(instance, ...) return sdk_def(instance, ...) end, })
    end

    rawset(class, key, bundle)
    symbol:cache_method(class, key, key)

    return bundle
end

--sdk的hook方法
function symbol.sdk_hook(method_bundle, pre_hook, post_hook)
    sdk.hook(method_bundle._typedef_, pre_hook, post_hook)
end

--自动提取给定组件的pre_MethodName和post_MethodName成员方法hook
function symbol.sdk_auto_hook(method_bundle, component)
    local pre_hook  = rawget(component, "pre_" .. method_bundle._typedef_:get_name())
    local post_hook = rawget(component, "post_" .. method_bundle._typedef_:get_name())

    sdk.hook(method_bundle._typedef_,
        pre_hook and function(parameters) return pre_hook(component, parameters) end or nil,
        post_hook and function(result) return post_hook(component, result) end or nil)
end

--预载所有需要使用的符号
function mhr:initialize()
    symbol:load()
end

--持久化所有符号，用于在下次进程启动或reframework重置后预载
function mhr:release()
    symbol:dump()
end

--mhr对象元表
local meta_instance = {
    __call     = function(self, instance)
        rawset(self, "_instance_", "table" == type(instance) and rawget(instance, "_instance_") or instance)
        return self
    end,
    __index    = function(self, key)
        local member = self._class_[key]
        return mhr_type.method == member._type_ and member._getter_ or member._getter_(self)
    end,
    __newindex = function(self, key, value)
        rawget(self, "_instance_")[key] = value
    end,
    __tostring = function(self)
        local instance = rawget(self, "_instance_")
        return self._class_._name_ .. "("
            .. (instance and string.format("0x%02x", instance:get_address()) or "nil")
            .. ")"
    end
}

--mhr元素对象元表
local meta_list_element = {
    __call     = meta_instance.__call,
    __index    = function(self, key)
        if "number" == type(key) then
            return rawget(self, "_get_")(self, key)
        end
        return meta_instance.__index(self, key)
    end,
    __newindex = function(self, key, value)
        if "number" == type(key) then
            rawget(self, "_set_")(self, key, value)
            return
        end
        meta_instance.__newindex(self, key, value)
    end,
    __tostring = meta_instance.__tostring,
    __pairs    = function(self)
        return rawget(self, "_next_"), self, nil
    end,
    __len      = function(self)
        return rawget(self, "_length_")(self)
    end,
}

--用于列表型集合
local collection_list = {
    meta   = meta_list_element,
    get    = function(self, index)
        if index > self:get_Count() then
            return nil
        end
        return self:get_Item(index - 1)
    end,
    set    = function(self, index, value)
        if index > self:get_Count() then
            error("数组越界")
        end
        self:set_Item(index - 1, value)
    end,
    pairs  = function(self)
        return rawget(self, "next"), self, nil
    end,
    next   = function(self, index)
        if not index then
            if self:get_Count() > 0 then
                return 1, self:get_Item(0)
            else
                return nil
            end
        end

        local next_index = index < self:get_Count() and index + 1 or nil
        return next_index, next_index and self:get_Item(index) or nil
    end,
    length = function(self)
        return self:get_Count()
    end,
}

local meta_map_element = {
    __call     = meta_instance.__call,
    __index    = function(self, key)
        local key_type = type(key)
        if "table" == key_type then
            return rawget(self, "_get_")(self, key:get_instance())
        end
        if "number" == key_type or "userdata" == key_type then
            return rawget(self, "_get_")(self, key)
        end

        return meta_instance.__index(self, key)
    end,
    __newindex = function(self, key, value)
        local key_type = type(key)
        if "table" == key_type then
            rawget(self, "_set_")(self, key:get_instance(), value)
            return
        end
        if "number" == key_type or "userdata" == key_type then
            rawget(self, "_set_")(self, key, value)
            return
        end

        meta_instance.__newindex(self, key, value)
    end,
    __tostring = meta_instance.__tostring,
    __pairs    = function(self)
        return rawget(self, "_next_"), self, nil
    end,
    __len      = function(self)
        return rawget(self, "_length_")(self)
    end,
}

--用于映射型集合
local collection_map = {
    meta   = meta_map_element,
    get    = function(self, index)
        return self:get_Item(index)
    end,
    set    = function(self, index, value)
        self:set_Item(index, value)
    end,
    pairs  = function(self)
        return rawget(self, "next"), self, nil
    end,
    next   = function(self, index)
        return nil, nil
    end,
    length = function(self)
        return self:get_Count()
    end,
}

local meta_library            = {}
local meta_template           = {}
local meta_namespace_or_class = {}

function meta_library:__index(key)
    local base_name = rawget(self, "_name_")
    local name      = base_name and (base_name .. "." .. key) or key
    local typedef   = sdk.find_type_definition(name)

    local namespace = {
        _type_ = mhr_type.namespace,
        _name_ = name,
    }

    --处理模板类型
    local template = meta_library:get_template_config(namespace)
    if template then
        rawset(self, key, meta_library:make_template(namespace, template))
        return namespace
    end

    --处理命名空间
    if not typedef then
        rawset(self, key, setmetatable(namespace, meta_namespace_or_class))
        return namespace
    end

    --处理枚举和常规类型
    namespace._typedef_ = typedef
    namespace = meta_library:is_enum(namespace)
        and meta_library:make_enum(namespace) or meta_library:make_class(namespace)
    rawset(self, key, namespace)

    return namespace
end

function meta_library:__newindex(key, _)
    error("invalid use: setting " .. key)
end

function meta_library:__tostring()
    return self._type_ .. ": mhr"
end

--获取模板类型配置
function meta_library:get_template_config(namespace)
    local templates = {
        { name = "snow.gui.fsm.GuiFsmBaseManager", arguments = 1, },
        { name = "snow.PosSetDataBase", arguments = 1, },
        { name = "System.Nullable", arguments = 1, },
        { name = "System.Array", arguments = 1, collection = collection_list, },
        { name = "System.Collections.Generic.List", arguments = 1, collection = collection_list, },
        { name = "System.Collections.Generic.Dictionary", arguments = 2, collection = collection_map, },
    }

    for _, template in pairs(templates) do
        if template.name == namespace._name_ then
            return template
        end
    end

    return nil
end

--生成模板类型
function meta_library:make_template(namespace, config)
    namespace._type_         = mhr_type.template
    namespace._arguments_    = config.arguments
    namespace._collection_   = config.collection
    namespace._instantiated_ = {}

    return setmetatable(namespace, meta_template)
end

--检测给定类型是否为枚举类型
function meta_library:is_enum(namespace)
    return namespace._typedef_:is_value_type() and "System.Enum" == namespace._typedef_:get_parent_type():get_full_name()
end

--生成枚举类型
function meta_library:make_enum(namespace)
    namespace._type_     = mhr_type.enum
    namespace.fetch_enum = symbol.fetch_enum

    return setmetatable(namespace, meta_namespace_or_class)
end

--生成常规类型
function meta_library:make_class(namespace)
    namespace._type_ = mhr_type.class

    namespace.get_singleton = symbol.get_singleton
    namespace.fetch_method  = symbol.fetch_method
    namespace.new           = symbol.new

    return setmetatable(namespace, meta_namespace_or_class)
end

--模板实例化
function meta_template:__call(...)
    local parameters = { ... }
    if mhr_type.instance == rawget(parameters[1], "_type_") then
        return parameters[1]._class_(parameters[1])
    end

    --组装实例化参数
    local argument = ""
    for _, parameter in pairs(parameters) do
        argument = argument .. "," .. parameter._name_
    end
    argument = string.sub(argument, 2)

    return meta_template:make_instanted(self, argument)
end

function meta_template:__tostring()
    return self._type_ .. ": " .. self._name_
end

function meta_template:__index(key)
    return meta_template:make_instanted(self, key)
end

function meta_template:__newindex(key, _)
    error("invalid use: setting " .. key)
end

--生成模板的实例化类型
function meta_template:make_instanted(template, argument)
    local class = template._instantiated_[argument]
    if class then
        return class
    end

    --组装实例化类型名称
    local name = "System.Array" == template._name_
        and (argument .. "[]")
        or string.format("%s`%d<%s>", template._name_, template._arguments_, argument)

    class = {
        _name_       = name,
        _typedef_    = sdk.find_type_definition(name),
        _generic_    = template._name_,
        _argument_   = argument,
        _collection_ = rawget(template, "_collection_"),
    }
    template._instantiated_[argument] = meta_library:make_class(class)

    return class
end

function meta_namespace_or_class:__call(instance)
    if not instance then
        return nil
    end

    --构造常规对象
    local mhr_instance = {
        _type_     = mhr_type.instance,
        _instance_ = "table" == type(instance) and rawget(instance, "_instance_") or instance,
        _class_    = self,

        get_address  = function(self)
            local instance = rawget(self, "_instance_")
            return instance and instance:get_address() or 0
        end,
        get_instance = function(self)
            return rawget(self, "_instance_")
        end,
    }

    --检测是否为集合对象
    local collection = rawget(self, "_collection_")
    if not collection then
        return setmetatable(mhr_instance, meta_instance)
    end

    --装入集合方法
    mhr_instance._next_   = collection.next
    mhr_instance._length_ = collection.length
    mhr_instance._get_    = collection.get
    mhr_instance._set_    = collection.set

    return setmetatable(mhr_instance, collection.meta)
end

function meta_namespace_or_class:__tostring()
    return self._type_ .. ": " .. self._name_
end

function meta_namespace_or_class:__index(key)
    --检测当前mhr命名空间是否为sdk类型
    local typedef = rawget(self, "_typedef_")
    if not typedef then
        return meta_library.__index(self, key)
    end

    --检测给定key是否为方法
    local member = typedef:get_method(key)
    if member then
        return symbol.fetch_method(self, key, nil, member)
    end

    --检测给定key是否为属性
    member = self._typedef_:get_field(key)
    if not member then
        return meta_library.__index(self, key)
    end

    --检测给定属性是否是枚举类型值
    if mhr_type.enum == self._type_ and member:is_static() then
        return symbol.fetch_enum(self, key, nil, member)
    end
    --处理非枚举类型的属性
    return symbol.fetch_attribute(self, key, member)
end

function meta_namespace_or_class:__newindex(key, _)
    error("invalid use: setting " .. key)
end

setmetatable(mhr, meta_library)

return mhr
